package nkr1pt.popkorn.download;

import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import nkr1pt.popkorn.exceptions.RecoverableException;
import nkr1pt.popkorn.helper.FileUtils;
import nkr1pt.popkorn.helper.URLWrapper;
import org.apache.commons.io.FilenameUtils;

/**
 *
 * @author Kristof Vanhaeren
 */
public class DownloadCandidate {

    public enum Host {

        RAPIDSHARE;
    }
    private Host host;
    private List<DownloadCandidateItem> items = new ArrayList<DownloadCandidateItem>();
    private File file;
    private File metaInformation;
    private String currentLocation;
    private String name;
    private String archivePassword;
    private String waitingLocation;
    private String inProgressLocation;
    private String completedLocation;
    private String failedLocation;
    private String extractionFailedLocation;
    private DownloadCandidateState state;
    private Set<DownloadCandidateItem> corruptedItems = new HashSet<DownloadCandidateItem>();
    private DownloadCandidateItemComparator downloadCandidateItemComparator = new DownloadCandidateItemComparator();

    public DownloadCandidate(File file) {
        this.file = file;
        this.name = FilenameUtils.removeExtension(FilenameUtils.getName(file.getName()));
    }

    public Host getHost() {
        return host;
    }

    public void setHost(Host host) {
        this.host = host;
    }

    public File getFile() {
        return file;
    }

    public void setFile(File file) {
        this.file = file;
    }

    public File getMetaInformation() {
        return metaInformation;
    }

    public void setMetaInformation(File metaInformation) {
        this.metaInformation = metaInformation;
    }

    public String getCurrentLocation() {
        return currentLocation;
    }

    public void setCurrentLocation(String location) {
        if (location.equals(getWaitingLocation())) {
            currentLocation = getWaitingLocation();
            return;
        }

        File fileDir = new File(FilenameUtils.getFullPath(file.getAbsolutePath()));
        if (fileDir.getName().contains(file.getName())) {
            File dest = new File(location);
            if (!FilenameUtils.getFullPath(fileDir.getAbsolutePath()).contains(dest.getAbsolutePath())) {
                FileUtils.moveDirectoryToDirectory(fileDir, dest);
                file = new File(dest.getPath() + File.separator + file.getName() + File.separator + file.getName());
                metaInformation = new File(dest.getPath() + File.separator + file.getName() + File.separator + metaInformation.getName());
            }
        } else {
            File dest = new File(location + File.separator + name);
            if (!FilenameUtils.getFullPath(file.getAbsolutePath()).contains(dest.getAbsolutePath())) {
                FileUtils.moveFileToDirectory(file, dest);
                FileUtils.moveFileToDirectory(metaInformation, dest);
                file = new File(dest.getPath() + File.separator + file.getName());
                metaInformation = new File(dest.getPath() + File.separator + metaInformation.getName());
            }
        }

        this.currentLocation = location;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void addItem(DownloadCandidateItem item) {
        Host h = findHost(item.getUrl());
        if (host != null && !host.equals(h)) {
            throw new RecoverableException("Links in file are inconsistent, they should all point to the same host.");
        }

        items.add(item);
        host = h;
    }

    public List<DownloadCandidateItem> getItems() {
        Collections.sort(items, downloadCandidateItemComparator);
        return items;
    }

    public String getArchivePassword() {
        return archivePassword;
    }

    public void setArchivePassword(String archivePassword) {
        this.archivePassword = archivePassword;
    }

    public String getInProgressLocation() {
        return inProgressLocation;
    }

    public void setInProgressLocation(String inProgressLocation) {
        this.inProgressLocation = inProgressLocation;
    }

    public String getCompletedLocation() {
        return completedLocation;
    }

    public void setCompletedLocation(String completedLocation) {
        this.completedLocation = completedLocation;
    }

    public String getExtractLocation() {
        return completedLocation + File.separator + name;
    }

    public String getExtractionFailedLocation() {
        return extractionFailedLocation;
    }

    public void setExtractionFailedLocation(String extractionFailedLocation) {
        this.extractionFailedLocation = extractionFailedLocation;
    }

    public String getFailedLocation() {
        return failedLocation;
    }

    public void setFailedLocation(String failedLocation) {
        this.failedLocation = failedLocation;
    }

    public String getWaitingLocation() {
        return waitingLocation;
    }

    public void setWaitingLocation(String waitingLocation) {
        this.waitingLocation = waitingLocation;
    }

    public DownloadCandidateState getState() {
        if (state != null) {
            return state;
        } else if (isComplete()) {
            state = DownloadCandidateState.COMPLETE;
        } else if (isFailed()) {
            state = DownloadCandidateState.FAILED;
        } else if (isInProgress()) {
            state = DownloadCandidateState.INPROGRESS;
        } else if (isWaiting()) {
            state = DownloadCandidateState.WAITING;
        }

        return state;
    }

    public void setState(DownloadCandidateState state) {
        this.state = state;
    }

    public boolean isComplete() {
        for (DownloadCandidateItem item : items) {
            if (!(item.getState().equals(DownloadCandidateState.COMPLETE))) {
                return false;
            }
        }

        return true;
    }

    public boolean isFailed() {
        for (DownloadCandidateItem item : items) {
            if (item.getState().equals(DownloadCandidateState.FAILED)) {
                return true;
            }
        }

        return false;
    }

    public boolean isInProgress() {
        for (DownloadCandidateItem item : items) {
            if (item.getState().equals(DownloadCandidateState.INPROGRESS)) {
                return true;
            }
        }

        return false;
    }

    public boolean isWaiting() {
        for (DownloadCandidateItem item : items) {
            if (!item.getState().equals(DownloadCandidateState.WAITING)) {
                return false;
            }
        }

        return true;
    }

    public boolean isInvalid() {
        return getState().equals(DownloadCandidateState.INVALID);
    }

    public float getProgress() {
        int total = items.size();
        int completed = 0;
        for (DownloadCandidateItem item : items) {
            if (item.getState().equals(DownloadCandidateState.COMPLETE)) {
                completed++;
            }
        }

        float decimals = (float) Math.pow(10, 2);
        float res = (float) completed / total * 100 * decimals;
        res = Math.round(res);
        return res / decimals;
    }

    /** the size in bytes */
    public long getSize() {
        long size = 0L;
        for (DownloadCandidateItem item : items) {
            if (item.getSize() == -1) {
                return -1;
            }
            size += item.getSize();
        }
        return size;
    }

    public boolean isEligibleToDownload() {
        return getState().equals(DownloadCandidateState.WAITING);
    }

    public boolean isExtractable() {
        for (DownloadCandidateItem item : items) {
            if (item.getName().endsWith(".rar")) {
                return true;
            }
        }

        return false;
    }

    public void addCorruptedItems(Set<DownloadCandidateItem> items) {
        corruptedItems.addAll(items);
    }

    public Set<DownloadCandidateItem> getCorruptedItems() {
        return corruptedItems;
    }

    public void removeItems(Set<DownloadCandidateItem> items) {
        for (DownloadCandidateItem item : items) {
            FileUtils.removeFile(new File(item.getAbsolutePath()));
        }

        this.items.removeAll(items);
    }

    private Host findHost(URLWrapper url) {
        if (url.toString().contains("rapidshare")) {
            return Host.RAPIDSHARE;
        }

        throw new RecoverableException("Ignoring file " + this.getName() + " because host could not be determined from URL " + url);
    }

    private class DownloadCandidateItemComparator implements Comparator {

        public int compare(Object o1, Object o2) {
            DownloadCandidateItem item1 = (DownloadCandidateItem)o1;
            DownloadCandidateItem item2 = (DownloadCandidateItem)o2;
            return item1.getName().compareTo(item2.getName());
        }

    }
}